package com.evil.common.es.util;

import co.elastic.clients.elasticsearch._types.aggregations.*;
import com.evil.common.es.dto.AggTreeParam;
import com.evil.common.es.dto.AggTreeResult;
import com.evil.common.es.dto.Summary;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * es工具
 *
 * @author liyang
 * @date 2022-08-08 00:00
 */
@Slf4j
public class ElasticsearchUtil {

    public static final String NESTED = "NESTED";
    public static final String CONTROL = "CONTROL";
    public static final String VALUE = "VALUE";
    public static final String REV = "REV";

    public static String nested(String title) {
        return ESFieldUtil.underJoin(title, NESTED);
    }

    public static String control(String title) {
        return ESFieldUtil.underJoin(title, CONTROL);
    }

    public static String value(String title) {
        return ESFieldUtil.underJoin(title, VALUE);
    }

    public static String reValue(String title) {
        int length = (ESFieldUtil.UNDERLINE + VALUE).length();
        return null == title || title.length() < length ? title : title.substring(0, title.length() - length);
    }

    public static String rev(String title) {
        return ESFieldUtil.underJoin(title, REV);
    }

    /**
     * 解析汇总结果
     *
     * @param summary      summary
     * @param aggregations aggregations
     * @param <T>          <T>
     * @return Map
     */
    public static <T> Map<Summary<T>, Aggregate> aggregationsData(Summary<T> summary, Aggregate aggregations) {
        Object aggsObj = aggregations._get();
        Map<String, Aggregate> aggregateMap = new LinkedHashMap<>();
        if (aggsObj instanceof NestedAggregate agg) {
            aggregateMap = agg.aggregations();
        }
        // rev层
        else if (aggsObj instanceof ReverseNestedAggregate agg) {
            aggregateMap = agg.aggregations();
        }
        Map<Summary<T>, Aggregate> result = new LinkedHashMap<>();
        // nested 层
        String nestedTitle = ElasticsearchUtil.nested(summary.getTitle());
        if (aggregateMap.containsKey(nestedTitle)) {
            Aggregate nestedAggAggregate = aggregateMap.get(nestedTitle);
            NestedAggregate nestedAggregate = (NestedAggregate) nestedAggAggregate._get();

            Map<String, Aggregate> controlAggMap = nestedAggregate.aggregations();
            // control 层
            String controlTitle = ElasticsearchUtil.control(summary.getTitle());
            if (controlAggMap.containsKey(controlTitle)) {
                Aggregate controlAggAggregate = controlAggMap.get(controlTitle);
                FilterAggregate controlAggregate = (FilterAggregate) controlAggAggregate._get();

                Map<String, Aggregate> valueAggMap = controlAggregate.aggregations();
                // value 层
                String valueTitle = ElasticsearchUtil.value(summary.getTitle());
                if (valueAggMap.containsKey(valueTitle)) {
                    Aggregate valueAggAggregate = valueAggMap.get(valueTitle);
                    Object valueAggObj = valueAggAggregate._get();
                    // 分桶
                    if (valueAggObj instanceof StringTermsAggregate valueAggregate) {
                        List<StringTermsBucket> buckets = valueAggregate.buckets().array();
                        for (StringTermsBucket bucket : buckets) {
                            Map<String, Aggregate> revAggMap = bucket.aggregations();
                            // rev层
                            String revTitle = ElasticsearchUtil.rev(summary.getTitle());
                            if (revAggMap.containsKey(revTitle)) {
                                Aggregate aggregate = revAggMap.get(revTitle);
                                result.put(summary.copy(bucket.key().stringValue()), aggregate);
                            } else {
                                Aggregate revAggregate = ReverseNestedAggregate.of(e -> e.aggregations(revAggMap).docCount(revAggMap.size()))
                                        ._toAggregate();
                                result.put(summary.copy(bucket.key().stringValue()), revAggregate);
                            }
                        }
                    }
                    // 日期分桶
                    if (valueAggObj instanceof DateHistogramAggregate dateAggregate) {
                        List<DateHistogramBucket> buckets = dateAggregate.buckets().array();
                        for (DateHistogramBucket bucket : buckets) {
                            Map<String, Aggregate> revAggMap = bucket.aggregations();
                            // rev层
                            String revTitle = ElasticsearchUtil.rev(summary.getTitle());
                            if (revAggMap.containsKey(revTitle)) {
                                Aggregate aggregate = revAggMap.get(revTitle);
                                result.put(summary.copy(bucket.keyAsString()), aggregate);
                            } else {
                                Aggregate revAggregate = ReverseNestedAggregate.of(e -> e.aggregations(revAggMap).docCount(revAggMap.size()))
                                        ._toAggregate();
                                result.put(summary.copy(bucket.keyAsString()), revAggregate);
                            }
                        }
                    }
                    // 指标
                    if (valueAggObj instanceof ValueCountAggregate valueCountAggregate) {
                        result.put(summary.copy(String.valueOf(valueCountAggregate.value())), null);
                    }
                    // 指标
                    if (valueAggObj instanceof SingleMetricAggregateBase singleAggregate) {
                        result.put(summary.copy(singleAggregate.valueAsString()), null);
                    }
                }
            }
        }
        return result;
    }

    public static List<AggTreeResult> analysis(String title, AggTreeParam<?> param, Aggregate aggregations) {
        List<AggTreeResult> result = new ArrayList<>();

        if (aggregations._get() instanceof SingleBucketAggregateBase agg) {
            agg.aggregations().forEach((k, v) -> result.addAll(analysis(k, param, v)));
        }
        // 分桶 === 普通分桶 value层
        else if (aggregations._get() instanceof StringTermsAggregate agg) {
            agg.buckets().array().forEach(bucket -> {
                AggTreeResult aggTree = new AggTreeResult(param, reValue(title), bucket.key()._get());
                List<AggTreeResult> children = new ArrayList<>();
                bucket.aggregations().forEach((k, v) -> {
                    log.info("analysis|bucket.k:{}, children.k:{}", title, k);
                    children.addAll(analysis(k, param, v));
                });
                aggTree.setChildren(children);
                result.add(aggTree);
            });
        }
        // 分桶 === 日期分桶 value层
        else if (aggregations._get() instanceof DateHistogramAggregate agg) {
            agg.buckets().array().forEach(bucket -> {
                AggTreeResult aggTree = new AggTreeResult(param, reValue(title), bucket.keyAsString());
                List<AggTreeResult> children = new ArrayList<>();
                bucket.aggregations().forEach((k, v) -> {
                    log.info("analysis|bucket.k:{}, children.k:{}", title, k);
                    children.addAll(analysis(k, param, v));
                });
                aggTree.setChildren(children);
                result.add(aggTree);
            });
        }
        // 分桶 === 计数统计分桶 value层
        else if (aggregations._get() instanceof ValueCountAggregate agg) {
            AggTreeResult aggTree = new AggTreeResult(param, reValue(title), agg.value());
            result.add(aggTree);
        }
        // 分桶 === 统计分桶 value层
        else if (aggregations._get() instanceof SingleMetricAggregateBase agg) {
            AggTreeResult aggTree = new AggTreeResult(param, reValue(title), agg.value());
            result.add(aggTree);
        }
        return result;
    }
}
